home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
filesyst
/
ncpfs
/
ipxripd-.000
/
ipxripd-
/
ipxripd
/
ipxsapd.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-02-03
|
15KB
|
741 lines
/*
IPX service advertising daemon
Copyright (C) 1994, 1995 Ales Dryak <e-mail: A.Dryak@sh.cvut.cz>
Copyright (C) 1996, Volker Lendecke <lendecke@namu01.gwdg.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/ipx.h>
#include "ipxutil.h"
#include "ipxkern.h"
#include "ipxsap.h"
#include "ipxd.h"
typedef unsigned char ifc_timer;
struct sap_table
{
ser_name_t name;
ser_type_t type;
hop_t hops;
struct sockaddr_ipx addr;
ifc_timer timers[MAX_IFACE]; /* 0<timer<EXPIRE_TIME => net is
reachable via iface */
/* timer>=EXPIPE_TIME => net is not r. */
struct sap_table *next;
};
struct sap_table *stable = NULL;
static IPXNet
ifc_net(struct ipx_interface *ifc)
{
return ntohl(ifc->s_output.dest_addr.sipx_network);
}
static int
is_expired(ifc_timer * tm)
{
return *tm >= EXPIRE_TIME;
}
static void
setup_timers(struct sap_table *rt, struct ipx_interface *ifc)
{
ifc_timer *tm;
for (tm = rt->timers; tm < rt->timers + MAX_IFACE; tm++)
{
*tm = EXPIRE_TIME;
}
rt->timers[ifc_get_index(ifc)] = 0;
}
static void
output_flushall()
{
struct ipx_interface *ifc = first_interface();
while (ifc != NULL)
{
ipx_sap_output_flush(&(ifc->s_output));
ifc = next_interface(ifc);
}
}
static void
output_set_destination(struct ipx_interface *ifc,
IPXNode dest_node, IPXPort dest_port)
{
if (ifc == NULL)
{
ifc = first_interface();
while (ifc != NULL)
{
ipx_sap_output_set_destination(&(ifc->s_output),
dest_node, dest_port);
ifc = next_interface(ifc);
}
}
else
{
ipx_sap_output_set_destination(&(ifc->s_output), dest_node,
dest_port);
}
}
static void
output_broadcast(struct sap_table *st, int down_allow)
{
struct ipx_interface *ifc = first_interface();
while (ifc != NULL)
{
if (is_expired(&(st->timers[ifc_get_index(ifc)])))
{
ipx_sap_output_response(&(ifc->s_output),
st->type, st->name,
&(st->addr),
st->hops, down_allow);
}
ifc = next_interface(ifc);
}
}
static int
output_sendto(int sock, void *buffer, int size, struct sockaddr_ipx *daddr)
{
int res;
DL_ENTRY;
DL_START;
fprintf(log_file, "Sending SAP to ");
ipx_fprint_saddr(log_file, daddr);
fprintf(log_file, "\n");
ipx_sap_fdump(log_file, buffer, size);
DL_END;
res = sendto(sock, (void *) buffer, size, 0,
(struct sockaddr *) daddr, sizeof(*daddr));
if (res == -1)
{
LOG_START;
fprintf(log_file, "sendto: %s\n", strerror(errno));
LOG_END;
}
return res;
}
static void
fprint_server(FILE * file, struct sap_table *st)
{
int i;
LOG_START;
fprintf(file, "SAP: type: %04X name: ", st->type);
ipx_sap_fprint_name(file, st->name);
fprintf(file, " \nhops: %i addr: ", st->hops);
ipx_fprint_saddr(file, &(st->addr));
fprintf(file, " ");
for (i = first_ifc_index(); i >= 0; i = next_ifc_index(i))
{
ifc_timer *tm = &(st->timers[i]);
fprintf(file, "%i", is_expired(tm) ? 0 : 1);
DL_START;
fprintf(file, "(%i)", (int) *tm);
DL_END;
}
LOG_END;
}
void
fdump_servers(FILE * file)
{
struct sap_table *st;
LOG_START;
fprintf(file, "IPX server database:\n");
for (st = stable; st != NULL; st = st->next)
{
fprint_server(file, st);
fprintf(file, "\n");
}
LOG_END;
}
static struct sap_table *
add_server(ser_type_t type, ser_name_t name, struct sockaddr_ipx *addr,
hop_t hops, struct ipx_interface *ifc)
{
struct sap_table *st;
st=(struct sap_table *)malloc(sizeof(struct sap_table));
if (st == NULL)
{
LOG_START;
fprintf(log_file, "ipxsapd: out of memory in add_server\n");
LOG_END;
return NULL;
}
st->type = type;
ipx_sap_assign_ser_name(st->name, name);
st->addr = *addr;
st->hops = hops;
setup_timers(st, ifc);
st->next = stable;
stable = st;
return st;
}
static void
delete_server(struct sap_table *d)
{
struct sap_table **s;
for (s = &stable; *s != NULL; s = &((*s)->next))
{
if (*s == d)
{
*s = d->next;
free(d);
return;
}
}
}
static void
handle_sap_gns_request(struct sap_entry * se, struct ipx_interface *src_ifc)
{
struct sap_table *cur;
struct sap_table *nearest = NULL;
if (ntohs(se->ser_type) == IPX_SAP_GENERAL_RQ)
{
return;
}
for (cur = stable; cur != NULL; cur = cur->next)
{
if (ipx_sap_type_equal(cur->type, ntohs(se->ser_type)))
{
if (nearest == NULL)
{
nearest = cur;
continue;
}
if (nearest->hops > cur->hops)
{
nearest = cur;
}
}
}
if (nearest != NULL)
{
ipx_sap_output_gns_response(&(src_ifc->s_output),
nearest->type, nearest->name,
&(nearest->addr), nearest->hops);
}
else
{
DL_START;
fprintf(log_file, "No servers of type %04X was found\n",
ntohs(se->ser_type));
DL_END;
}
}
static void
handle_sap_request(struct sap_entry *se, struct ipx_interface *src_ifc)
{
struct sap_table *cur;
for (cur = stable; cur != NULL; cur = cur->next)
{
if (ipx_sap_type_equal(cur->type, ntohs(se->ser_type)))
{
ipx_sap_output_response(&(src_ifc->s_output),
cur->type, cur->name,
&(cur->addr), cur->hops, 0);
}
}
}
static void
handle_sap_response(struct sap_entry *se, struct ipx_interface *src_ifc,
IPXNode src_node)
{
struct sap_table *cur;
se->hops = ntohs(se->hops) + 1;
for (cur = stable; cur != NULL; cur = cur->next)
{
if ( !ipx_sap_name_equal(cur->name, se->ser_name)
|| !ipx_sap_type_equal(cur->type, ntohs(se->ser_type)))
{
/* Another server was announced */
continue;
}
/* entry found */
if (se->hops <= IPX_SAP_SERVER_DOWN)
{
/* server ok */
if (se->hops > cur->hops)
{
return;
}
if (se->hops == cur->hops)
{
/* server has equal 'route' */
/* update info (near not neccesary) */
cur->addr.sipx_network = se->network;
ipx_assign_node(cur->addr.sipx_node, se->node);
cur->addr.sipx_port = se->port;
/* update timer for iface */
cur->timers[ifc_get_index(src_ifc)] = 0;
return;
}
/* server has better 'route' */
LOG_START;
fprintf(log_file, "CHANGE ");
fprint_server(log_file, cur);
LOG_END;
/* update table */
cur->hops = se->hops;
cur->addr.sipx_network = se->network;
ipx_assign_node(cur->addr.sipx_node, se->node);
cur->addr.sipx_port = se->port;
setup_timers(cur, src_ifc);
LOG_START;
fprintf(log_file, "\nto ");
fprint_server(log_file, cur);
fprintf(log_file, "\n");
LOG_END;
/* send info bcast */
output_broadcast(cur, 0);
}
else
{
/* server down through iface */
int src_ifc_idx = ifc_get_index(src_ifc);
int i;
cur->timers[src_ifc_idx] = EXPIRE_TIME;
for (i = first_ifc_index();
i >= 0;
i = next_ifc_index(i))
{
ifc_timer *tm = &(cur->timers[i]);
if (!is_expired(tm))
{
return;
}
}
cur->timers[src_ifc_idx] = 0;
cur->hops = IPX_SAP_SERVER_DOWN;
output_broadcast(cur, 1);
LOG_START;
fprintf(log_file, "DELETE ");
fprint_server(log_file, cur);
fprintf(log_file, " (service down)\n");
LOG_END;
delete_server(cur);
}
return;
}
/* entry not found */
if (se->hops <= IPX_SAP_SERVER_DOWN)
{
struct sap_table *st;
struct sockaddr_ipx addr;
addr.sipx_network = se->network;
ipx_assign_node(addr.sipx_node, se->node);
addr.sipx_port = se->port;
addr.sipx_type = 0;
st = add_server(ntohs(se->ser_type), se->ser_name, &addr,
se->hops, src_ifc);
if (st != NULL)
{
/* send info bcast */
output_broadcast(st, 0);
LOG_START;
fprintf(log_file, "ADD ");
fprint_server(log_file, st);
fprintf(log_file, "\n");
LOG_END;
}
}
}
void
handle_sap(struct sap_packet * pkt, int len, struct sockaddr_ipx *sipx,
struct ipx_interface *src_ifc)
{
struct sap_entry *se = pkt->sap_entries;
int nent = (len - 2) / sizeof(struct sap_entry);
if (ifc_net(src_ifc) != ntohl(sipx->sipx_network))
{
LOG_START;
fprintf(log_file, "SAP from non-local net ");
ipx_fprint_network(log_file, ntohl(sipx->sipx_network));
fprintf(log_file, " (ignored)\n");
LOG_END;
return;
}
if (len < 2)
{
LOG_START;
fprintf(log_file,
"SAP packet too small len=%i (ignored)\n", len);
LOG_END;
return;
}
if ( ipx_node_equal(src_ifc->ifnode, sipx->sipx_node)
&& ((unsigned short) sipx->sipx_port == htons(IPX_SAP_PORT)))
{
DL_START;
fprintf(log_file, "My packet (ignored)\n");
DL_END;
return;
}
switch (ntohs(pkt->operation))
{
case IPX_SAP_OP_REQUEST:
if (len != ipx_sap_size(1, IPX_SAP_OP_REQUEST))
{
LOG_START;
fprintf(log_file,
"SAP packet invalid size (ignored)\n");
LOG_END;
return;
}
output_set_destination(src_ifc,
sipx->sipx_node,
ntohs(sipx->sipx_port));
handle_sap_request(se, src_ifc);
output_flushall();
break;
case IPX_SAP_OP_GNS_REQUEST:
if (len != ipx_sap_size(1, IPX_SAP_OP_REQUEST))
{
LOG_START;
fprintf(log_file,
"SAP packet invalid size (ignored)\n");
LOG_END;
return;
}
output_set_destination(src_ifc,
sipx->sipx_node,
ntohs(sipx->sipx_port));
handle_sap_gns_request(se, src_ifc);
output_flushall();
break;
case IPX_SAP_OP_RESPONSE:
/* option: ignore responses from non SAP ports
* if (sipx->sipx_port!=htons(IPX_SAP_PORT)) return;
*/
output_set_destination(NULL, IPX_BROADCAST, IPX_SAP_PORT);
for (; nent--; se++)
{
handle_sap_response(se, src_ifc, sipx->sipx_node);
}
output_flushall();
break;
case IPX_SAP_OP_GNS_RESPONSE:
LOG_START;
fprintf(log_file,
"GNS response should never be received (ignored)\n");
LOG_END;
break;
default:
LOG_START;
fprintf(log_file, "Unknown SAP operation\n");
LOG_END;
break;
}
}
static void
delete_invalid_servers(void)
{
struct sap_table **s = &stable;
while (*s != NULL)
{
if ((*s)->hops >= IPX_SAP_SERVER_DOWN)
{
struct sap_table *d = *s;
*s = (*s)->next;
free(d);
}
else
{
s = &((*s)->next);
}
}
}
void
ipx_sap_do_aging(int rate, int do_broadcast)
{
struct sap_table *cur;
int servers_died = 0;
DL_START;
fprintf(log_file, "DO SAP AGING\n");
DL_END;
output_set_destination(NULL, IPX_BROADCAST, IPX_SAP_PORT);
for (cur = stable; cur != NULL; cur = cur->next)
{
int down = 1;
int i;
for (i = first_ifc_index(); i >= 0; i = next_ifc_index(i))
{
ifc_timer* tm = &(cur->timers[i]);;
if (!is_expired(tm))
{
(*tm) += rate;
if (!is_expired(tm))
{
down = 0;
}
}
}
if (down)
{
/* server is down */
LOG_START;
fprintf(log_file, "DELETE ");
fprint_server(log_file, cur);
fprintf(log_file, " (timed out)\n");
LOG_END;
/* send info bcast */
cur->hops = IPX_SAP_SERVER_DOWN;
output_broadcast(cur, 1);
/* table update deferred */
servers_died = 1;
} else
{
if (do_broadcast != 0)
{
output_broadcast(cur, 0);
}
}
}
/* Delete bogus servers */
if (servers_died != 0)
{
delete_invalid_servers();
}
output_flushall();
}
int
ipx_sap_init_ifc(struct ipx_interface *ifc, IPXNet network,
char *device, int type, void *data)
{
struct sockaddr_ipx sipx;
if (ipx_sap_output_init(&(ifc->s_output), network) != 0)
{
LOG_START;
fprintf(log_file,
"out of memory allocating output buffer\n");
LOG_END;
return -1;
}
if ((ifc->s_output.sk = socket(AF_IPX, SOCK_DGRAM, PF_IPX)) < 0)
{
LOG_START;
fprintf(log_file,
"can't open socket: %s\n", strerror(errno));
LOG_END;
return -1;
}
memset(&sipx, 0, sizeof(sipx));
sipx.sipx_family = AF_IPX;
sipx.sipx_network = htonl(network);
ipx_assign_node(sipx.sipx_node, IPX_THIS_NODE);
sipx.sipx_port = htons(IPX_SAP_PORT);
sipx.sipx_type = IPX_SAP_PTYPE;
if (bind(ifc->s_output.sk, (struct sockaddr *)&sipx, sizeof(sipx)) < 0)
{
LOG_START;
fprintf(log_file, "can't bind socket: %s\n",
strerror(errno));
LOG_END;
return -1;
}
if (ipx_kern_enable_broadcast(ifc->s_output.sk) != 0)
{
LOG_START;
fprintf(log_file, "cant' enable broadcast\n");
LOG_END;
exit(1);
}
LOG_START;
ipx_fprint_network(log_file, network);
fprintf(log_file,":");
ipx_fprint_node(log_file, ifc->ifnode);
fprintf(log_file,"\n");
LOG_END;
return 0;
}
static void
ipx_sap_deinit_ifc(struct ipx_interface *ifc)
{
close(ifc->s_output.sk);
}
void
ipx_sap_down_ifc(struct ipx_interface *ifc)
{
struct sap_table *st;
int ifc_index = ifc_get_index(ifc);
LOG_ENTRY;
LOG_START;
fprintf(log_file, "SAP DOWN INTERFACE ");
ipx_fprint_network(log_file, ifc_net(ifc));
fprintf(log_file, "\n");
LOG_END;
for (st = stable; st != NULL; st = st->next)
{
int i;
st->timers[ifc_index] = EXPIRE_TIME;
for (i = first_ifc_index();
i >= 0;
i = next_ifc_index(i))
{
ifc_timer *tm = &(st->timers[i]);
if (!is_expired(tm))
{
return;
}
}
st->timers[ifc_index] = 0;
st->hops = IPX_SAP_SERVER_DOWN;
output_broadcast(st, 1);
LOG_START;
fprintf(log_file, "DELETE ");
fprint_server(log_file, st);
fprintf(log_file, " (interface down)\n");
LOG_END;
}
delete_invalid_servers();
ipx_sap_deinit_ifc(ifc);
}
void
ipx_sap_initial_broadcasts()
{
struct ipx_interface *ifc;
ipx_sap_output_func = output_sendto;
output_set_destination(NULL, IPX_BROADCAST, IPX_SAP_PORT);
ifc = first_interface();
while (ifc != NULL)
{
ipx_sap_output_request(&(ifc->s_output),
IPX_SAP_GENERAL_RQ);
ifc = next_interface(ifc);
}
output_flushall();
}
void
ipx_sap_initial_broadcast(struct ipx_interface *ifc)
{
struct sap_table *cur;
output_set_destination(ifc, IPX_BROADCAST, IPX_SAP_PORT);
for (cur = stable; cur != NULL; cur = cur->next)
{
ipx_sap_output_response(&(ifc->s_output),
cur->type, cur->name,
&(cur->addr), cur->hops, 0);
}
ipx_sap_output_request(&(ifc->s_output), IPX_SAP_GENERAL_RQ);
ipx_sap_output_flush(&(ifc->s_output));
}
void
ipx_sap_done()
{
struct sap_table *cur;
LOG_ENTRY;
LOG_START;
fprintf(log_file, "SAP Shutdown start\n");
LOG_END;
output_set_destination(NULL, IPX_BROADCAST, IPX_SAP_PORT);
for (cur = stable; cur != NULL; cur = cur->next)
{
LOG_START;
fprintf(log_file, "DELETE ");
fprint_server(log_file, cur);
fprintf(log_file, " (shutdown)\n");
LOG_END;
/* send info bcast */
cur->hops = IPX_SAP_SERVER_DOWN;
output_broadcast(cur, 1);
/* update table */
delete_server(cur);
}
output_flushall();
LOG_ENTRY;
LOG_START;
fprintf(log_file, "SAP Shutdown end\n");
LOG_END;
}